package designpattern;

import java.util.ArrayList;
import java.util.List;

/**
 * 观察者模式实现
 * 1. 简介
 * 也被称为发布/订阅模式,当一个对象被修改时，则会自动通知依赖他的对象
 * <p>
 * 主要解决: 一个对象状态改变给其它对象通知的问题，并且要考虑到易用和低耦合，保证高度的写作
 * 何时使用: 一个对象（目标对象）的状态发生改变，所有的依赖对象（观察者对象）都将得到通知，进行广播通知。
 * 2. 优点:
 * (1) 观察者和被观察者时低耦合的
 * (2)  建立一套触发机制
 * 3. 缺点
 * (1) 当一个被观察者存在多个观察者时，将所有的观察者都通知会花费很多时间
 * (2) 当观察者和被观察者循环依赖时，可能导致系统奔溃
 * (3) 观察者模式没有相应的机制让观察者知道被观察者发生了什么变化
 * 4. 注意
 * (1) Java中已经存在对观察者模式支持的类
 * (2) 避免循环引用
 * (3) 一般采用异步的方式
 */
public class ObserverTest {
    public static void main(String[] args) {
        // 1. 定义被观察者角色
        Subject subject = new Subject();
        //2. 定义观察者类
        new BinaryObserver(subject);
        new HexaObserver(subject);
        System.out.println("第一次改变值");
        subject.setState(12);
        System.out.println("第二次改变值");
        subject.setState(16);
    }
}


// 1. 定义被观察者角色
class Subject {
    private Integer state;

    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
        // 当state值改变时唤醒观察者对象
        notifyAllObservers();
    }

    List<Observer> observers = new ArrayList<>();

    // 追加观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }

    // 唤醒所有观察者
    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

}

// 2. 定义抽象观察者角色
abstract class Observer {
    // 被观察者对象
    Subject subject;

    // 唤醒后调用的方法
    public abstract void update();
}

//3. 实体观察者类(BinaryObserver)
class BinaryObserver extends Observer {
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
        if (subject.getState() > 15) {
            System.out.println("大于15");
        } else {
            System.out.println("小于15");
        }
    }
}

//3. 实体观察者类(HexaObserver)
class HexaObserver extends Observer {
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
        System.out.println("Octal String: " + Integer.toOctalString(subject.getState()));
    }
}